/*                                                                      
 Copyright 2018 hzheng@gzhu.edu.cn              
                                                                         
 Licensed under the Apache License, Version 2.0 (the "License");         
 you may not use this file except in compliance with the License.        
 You may obtain a copy of the License at                                 
                                                                         
     http://www.apache.org/licenses/LICENSE-2.0                          
                                                                         
  Unless required by applicable law or agreed to in writing, software    
 distributed under the License is distributed on an "AS IS" BASIS,       
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and     
 limitations under the License.                                          
 */                                                                      
/*+***********************************************************
Filename: zh_exec_ctrl_v03.v
Description: main control module in core
Modification:
  2018.07.27 createion by Zheng Hui
       08.06 support more instructions
  2019.03.23 add load/store
  2019.04.02 debug, add inst_agu_group

************************************************************-*/
`include "e203_defines.v"

module zh_exec_ctrl_v03(
  input sys_clk,
  input rst_n,
  output core_clk,

  //pass to decoder
  input  [31:0] i_instr,
  
  //to regfile
  output [4:0] rf_rs1idx,
  output [4:0] rf_rs2idx,
  input  [31:0] i_rf_rs1data,
  input  [31:0] i_rf_rs2data,
  output [4:0] rf_rdidx,
  output rf_rdwen,
  output [31:0] rf_rd_data,

  //to alu
  output[31:0] alu_data_a,
  output[31:0] alu_data_b,
  output alu_unsigned_flag,  //signed/unsigned
  output alu_sub_flag,
  output alu_add_flag,
  output alu_and_flag,   
  output alu_or_flag,   
  output alu_xor_flag,
  output alu_sll_flag,   
  output alu_srl_flag,   
  output alu_sra_flag,
  output alu_compare_flag,
  output alu_enable,
  input [31:0] i_alu_result,
  input i_alu_equal_flag,   //a=b
  input i_alu_less_flag, //a<b
  input i_alu_greater_equal_flag, //a>=b

  //to instruction rom
  output [31:0] pc_value,

  //to data ram
  output [9:0] ram_addr,
  input [31:0] ram_din,
  output [31:0] ram_dout,
  output [3:0] ram_byte_en,
  output ram_wre

);

  wire dec_rs1x0;
  wire dec_rs2x0;
  wire dec_rs1en;
  wire dec_rs2en;
  wire dec_rdwen;
  wire [4:0] dec_rs1idx;
  wire [4:0] dec_rs2idx;
  wire [4:0] dec_rdidx;
  wire [`E203_DECINFO_WIDTH-1:0] dec_info;  
  wire [`E203_XLEN-1:0] dec_imm;

  wire dec_rv32;
  wire dec_bjp;
  wire dec_jal;
  wire dec_jalr;
  wire dec_bxx;
  wire [`E203_XLEN-1:0] dec_bjp_imm;

    //decoder module
    zh_decoder_v01 m_zh_decoder(
        .i_instr      (i_instr  ),
          
        .dec_rv32  (dec_rv32),
        .dec_bjp   (dec_bjp),
        .dec_jal   (dec_jal),
        .dec_jalr   (dec_jalr),
        .dec_bxx   (dec_bxx),
        .dec_bjp_imm(dec_bjp_imm),
        .dec_info  (dec_info ),
        .dec_rs1x0 (dec_rs1x0),
        .dec_rs2x0 (dec_rs2x0),
        .dec_rs1en (dec_rs1en),
        .dec_rs2en (dec_rs2en),
        .dec_rdwen (dec_rdwen),
        .dec_rs1idx(dec_rs1idx),
        .dec_rs2idx(dec_rs2idx),
        .dec_rdidx (dec_rdidx),
        .dec_imm(dec_imm)
    );

  //for alu operation
  //

  //retrieve reg data
  assign rf_rs1idx={5{dec_rs1en   }} & dec_rs1idx;
  assign rf_rs2idx={5{dec_rs2en   }} & dec_rs2idx;

  //wire result to alu input
  assign alu_data_a = i_rf_rs1data;

  //alu_data_b could be:
  // * i_rf_rs2data when add, sub, slt, sltu, and, or ,xor, sll, srl, sra
  // * dec_imm when addi, subi, slti, sltiu, andi, ori, xori, slli, srli, srai
  //assign alu_data_b = i_rf_rs2data;
//  assign alu_data_b = {32{dec_info[`E203_DECINFO_ALU_OP2IMM]}} & dec_imm) |
//                      {32{~dec_info[`E203_DECINFO_ALU_OP2IMM]}} & i_rf_rs2data);
  assign alu_data_b = dec_info[`E203_DECINFO_ALU_OP2IMM] ? dec_imm : i_rf_rs2data;

  //alu control

  //alu group
  //add/addi and sub/subi
  assign alu_add_flag = dec_info[`E203_DECINFO_ALU_ADD];
  assign alu_sub_flag = dec_info[`E203_DECINFO_ALU_SUB];
  //and/or/xor/andi/ori/xori
  assign alu_and_flag = dec_info[`E203_DECINFO_ALU_AND];
  assign alu_or_flag = dec_info[`E203_DECINFO_ALU_OR];
  assign alu_xor_flag = dec_info[`E203_DECINFO_ALU_XOR];
  //sll/srl/sra/slli/srli/srai
  assign alu_sll_flag = dec_info[`E203_DECINFO_ALU_SLL];
  assign alu_srl_flag = dec_info[`E203_DECINFO_ALU_SRL];
  assign alu_sra_flag = dec_info[`E203_DECINFO_ALU_SRA];
  //slt/slti
  wire slt_flag = dec_info[`E203_DECINFO_ALU_SLT];
  //sltu/sltiu
  wire sltu_flag = dec_info[`E203_DECINFO_ALU_SLTU];

  //bjp group
  wire beq_flag = dec_info[`E203_DECINFO_BJP_BEQ];
  wire bne_flag = dec_info[`E203_DECINFO_BJP_BNE];
  wire blt_flag = dec_info[`E203_DECINFO_BJP_BLT];
  wire bgt_flag = dec_info[`E203_DECINFO_BJP_BGT];
  wire bltu_flag = dec_info[`E203_DECINFO_BJP_BLTU];
  wire bgtu_flag = dec_info[`E203_DECINFO_BJP_BGTU];
  wire bxx_flag = dec_info[`E203_DECINFO_BJP_BXX];

  //alu_unsigned_flag
  assign alu_unsigned_flag = sltu_flag | bltu_flag | bgtu_flag;

  //instruction group
  wire inst_alu_group = (dec_info[`E203_DECINFO_GRP] == `E203_DECINFO_GRP_ALU); 
  wire inst_bjp_group = (dec_info[`E203_DECINFO_GRP] == `E203_DECINFO_GRP_BJP); 
  wire inst_agu_group = (dec_info[`E203_DECINFO_GRP] == `E203_DECINFO_GRP_AGU); //for load/store


  //lui
  wire lui_flag = inst_alu_group & dec_info[`E203_DECINFO_ALU_LUI];
  //auipc
  wire auipc_flag = inst_alu_group & dec_info[`E203_DECINFO_ALU_OP1PC];
  //nop
  wire nop_flag = inst_alu_group & dec_info[`E203_DECINFO_ALU_NOP];
  //jal, jalr
  wire jump_flag = inst_bjp_group & dec_info[`E203_DECINFO_BJP_JUMP];

  //slt and bxx need compare (actually sub)
  assign alu_compare_flag = (inst_alu_group & (slt_flag | sltu_flag)) |
                            (inst_bjp_group & bxx_flag);

  //assign alu_signed_flag = (inst_alu_group &  slt_flag) |
  //                          (inst_bjp_group & (blt_flag|bgt_flag));

  //assign alu_enable = (dec_info[`E203_DECINFO_GRP] == `E203_DECINFO_GRP_ALU); 
  //for alu group instructions and bxx instructions, need alu operation 
  assign alu_enable = inst_alu_group | alu_compare_flag;


  //ram: load/store
  //

  wire load_flag = inst_agu_group & dec_info[`E203_DECINFO_AGU_LOAD];
  wire store_flag = inst_agu_group & dec_info[`E203_DECINFO_AGU_STORE];
  wire [1:0] load_store_size = dec_info[`E203_DECINFO_AGU_SIZE];
  wire load_byte_sign = dec_info[`E203_DECINFO_AGU_USIGN];

  //addr = rs1 + imm
  wire [31:0] ram_addr_i = i_rf_rs1data + dec_imm;
  assign ram_addr[9:0] = ram_addr_i[11:2];

  //
  wire load_byte_flag = load_flag & (load_store_size == 2'b00);
  wire load_half_word_flag = load_flag & (load_store_size == 2'b01);
  wire load_word_flag = load_flag & (load_store_size == 2'b10);


  
  wire byte_offset_0 = (ram_addr_i[1:0] == 2'b00);
  wire byte_offset_1 = (ram_addr_i[1:0] == 2'b01);
  wire byte_offset_2 = (ram_addr_i[1:0] == 2'b10);
  wire byte_offset_3 = (ram_addr_i[1:0] == 2'b11);
  
  //handle input data

  //LB: 00, LH:01, LW:10
  wire [7:0] byte_value = byte_offset_0 ? ram_din[7:0] :
                          byte_offset_1 ? ram_din[15:8] :
                          byte_offset_2 ? ram_din[23:16] : ram_din[31:24];
  wire [31:0] extended_byte_value = load_byte_sign ? {{24{byte_value[7]}},byte_value[7:0]} :
                                              {24'b0,byte_value[7:0]};
						  
  wire [16:0] half_word_value = byte_offset_0 ? ram_din[15:0] :
                          byte_offset_1 ? ram_din[23:8] :
                          byte_offset_2 ? ram_din[31:16] : 16'b0; //cross word offset not supported
  wire [31:0] extended_half_word_value = load_byte_sign ? 
                     {{16{half_word_value[15]}},half_word_value[15:0]} :
                     {16'b0,half_word_value[15:0]};
  
  
  wire [31:0] load_ram_value = load_word_flag ? ram_din :
                   load_half_word_flag ? extended_half_word_value :
				   load_byte_flag ? extended_byte_value : 32'b0;


  //store instruction
  //write to ram
  wire store_byte_flag = store_flag & (load_store_size == 2'b00);
  wire store_half_word_flag = store_flag & (load_store_size == 2'b01);
  wire store_word_flag = store_flag & (load_store_size == 2'b10);

  wire [31:0] store_byte_value = byte_offset_0 ? {24'b0,i_rf_rs1data[7:0]} :
                                 byte_offset_1 ? {16'b0,i_rf_rs1data[7:0], 8'b0} :
								 byte_offset_2 ? {8'b0,i_rf_rs1data[7:0], 16'b0} :
								 {i_rf_rs1data[7:0], 16'b0};
  wire [3:0] store_byte_pos_byte = byte_offset_0 ? 4'b0001 :
                                 byte_offset_1 ? 4'b0010 :
								 byte_offset_2 ? 4'b0100 : 4'b1000 ;

  wire [31:0] store_half_word_value = byte_offset_0 ? {16'b0,i_rf_rs1data[15:0]} :
                                 byte_offset_1 ? {8'b0,i_rf_rs1data[15:0], 8'b0} :
								 byte_offset_2 ? {i_rf_rs1data[15:0], 16'b0} :
								 32'b0;
  wire [3:0] store_byte_pos_half_word = byte_offset_0 ? 4'b0011 :
                                 byte_offset_1 ? 4'b0110 :
								 byte_offset_2 ? 4'b1100 : 4'b0000 ;
  
  wire [31:0] store_ram_value = store_word_flag ? i_rf_rs1data :
         store_byte_flag ? store_byte_value : store_half_word_value;
  assign ram_dout = store_flag ? store_ram_value : 32'b0;
 
  assign ram_byte_en = store_word_flag ? 4'b1111 :
            store_byte_flag ? store_byte_pos_byte : store_byte_pos_half_word;
  assign ram_wre = store_flag & (~sys_clk);


  //
  //write back result to general register
  //
  assign rf_rdidx=dec_rdidx;
  assign rf_rdwen=dec_rdwen;

  //for alu operation other than slt/sltu, rf_rd_data=i_alu_result.
  //for slt/sltu, set rf_rd_data according to compare result.
  //assign rf_rd_data=i_alu_result;
  wire [31:0] rf_rd_data_alu_result;
  assign rf_rd_data_alu_result= (~alu_compare_flag) ? i_alu_result : 
                                    (i_alu_less_flag ? 32'd1 : 32'd0) ;



  //2 adder for reused by jump and auipc:
  // * pc_add_four: rd for jal/jalr
  //                next pc for normal instructions
  // * pc_add_imm:  next pc for jal
  //                next pc for branch in effect
  // * rs1_add_imm: next pc for jalr
  wire [31:0] adder_pc_add_four = pc_r + 32'd4;

  wire [31:0] pc_adder_op1 = dec_jalr ? i_rf_rs1data : pc_r;
  wire [31:0] adder_pc_or_rs1_add_imm = pc_adder_op1 + dec_imm;
  
  
  //rf_rd_data could be:
  // * alu result: add sub, shift, logical; slt/sltu: 1 or 0
  // * lui/auipc: imm or pc + imm
  // * jal/jalr: pc + 4
  // * load: load_ram_value
/*
  assign rf_rd_data = lui_flag ? dec_imm : 
                 (jump_flag) ? adder_pc_add_four:
                 (auipc_flag) ? adder_pc_or_rs1_add_imm :
                 alu_enable ? rf_rd_data_alu_result : 32'b0;
*/
// /*  
  assign rf_rd_data = lui_flag ? dec_imm : 
                 (jump_flag) ? adder_pc_add_four:
                 (auipc_flag) ? adder_pc_or_rs1_add_imm :
                 alu_enable ? rf_rd_data_alu_result : 
                 load_flag ? load_ram_value : 32'b0;
// */
  
  //
  //pc block
  //
  wire [31:0] pc_r;
  wire [31:0] pc_next;

  //next pc value could be:
  //jal: pc = pc + imm;
  //jalr: pc = i_rf_rs1data + imm;
  //beq: pc = pc + imm if i_alu_equal_flag
  //bne: pc = pc + imm if ~i_alu_equal_flag
  //blt, bltu: pc = pc + imm if i_alu_less_flag
  //bge, bgeu: pc = pc + imm if i_alu_greater_equal_flag
  //ecall, ebreak: not implemented yet
  //mret: pc = mepc, implement later
  //normal: pc = pc + 4

  wire bxx_in_effrect = dec_bxx & ( (beq_flag & i_alu_equal_flag) |
                        (bne_flag & (~i_alu_equal_flag)) |
                        ((blt_flag|bltu_flag) & i_alu_less_flag) |
                        ((bgt_flag|bgtu_flag) & i_alu_greater_equal_flag));
  wire bjp_in_effrect = dec_jal | dec_jalr | bxx_in_effrect;


  assign pc_next = bjp_in_effrect ? adder_pc_or_rs1_add_imm : adder_pc_add_four;

  //
  sirv_gnrl_dfflr #(32) pc_dfflr (1'b1, pc_next, pc_r, core_clk, rst_n);

  assign pc_value = pc_r;  



  //core clk generation

  wire [2:0] count_out;
  counter m_core_clk(
      .clk_in(sys_clk),
      .rst_n(rst_n),
        .count(count_out)
  );
  //assign core_clk = count_out[0]; //sys_clk/8
  assign core_clk = count_out[2]; //sys_clk/32

  //assign core_clk = sys_clk;


endmodule
